#include <iostream>
#include <iomanip>
#include <fstream>

#include <fcntl.h>

#include "IOError.h"
#include "Counter_DL636G1.h"
#include "DigitalIO_DL636F.h"
#include "TimeCounter_DL643A1.h"
#include "QDC_V965A.h"
#include "timemeasure.h"
#include <root/TROOT.h>
#include <root/TFile.h>
#include <root/TTree.h>

using namespace std;


const int measure_time = 300000; /* usec */
const char *device="/dev/sis1100_00remote";
const static u_int32_t USED_CHANNELS  = 0x0ff;
const static u_int32_t USED_THRESHOLD = 0x000;
const static u_int32_t MAX_EVENTS = 32;


int main(int argc, char *argv[])
{
  int crate;
  short adc[8];
  unsigned time = 0, dt = 0, prev_time = 0;
  // EventStruct ev;
  EventStruct ev[32];
  unsigned ev_counter = 0;
  unsigned buf_full_1 = 0;
  unsigned buf_full_2 = 0;
  
  cout << "dackel 0.0.0" << endl << endl;

  TM_INIT();
  
  // open sis1100/3100 pci-to-vme link
  if( !(crate=open(device, O_RDWR, 0)) ) {
    cout << "ERROR: could not open \"" << device << "\"" << endl;
    exit(1);
  }
  
  vmesysreset(crate);

  // instantiate counter object
  Counter_DL636G1 counter1(crate,0x8820);

  // instantiate io object
  DigitalIO_DL636F io1(crate,0x8800);

  TimeCounter_DL643A1 tc1(crate, 0x8860);
  
  // instantiate adc object
  QDC_V965A  qdc1(crate,0x25390000);
	
  string filename = "qdc_test.dat";
  cout << "saving results to " << filename << "..." << endl;		
  ofstream f(filename.c_str(), ios::trunc);
	
  // init root file
  TFile rf("qdc_test.root", "recreate");
  TTree * tree = new TTree("Data", "Data");
  tree->Branch("adc", &(adc[0]), "adc[8]/S");
  tree->Branch("time", &(time), "time/I");
  tree->Branch("dt", &(dt), "dt/I");
	

 try {
	  
	tc1.reset();
	counter1.reset();
	
	// output counter values
	cout << "counter1.a == " << counter1.getA() << endl;
	cout << "counter1.b == " << counter1.getB() << endl;
	cout << "counter1.c == " << counter1.getC() << endl << endl;
	
	// init qdc1.
	cout << "qdc1: init" << endl;
	qdc1.SoftwareReset(); 
	qdc1.DefineChannelBehaviour(USED_CHANNELS, USED_THRESHOLD);
	qdc1.CountOnlyValidEvents(); 
	qdc1.SetFastClearWindow(0);
	qdc1.SetBit2Pattern(QDC_V965A::BITSET_2_OVER_RANGE_ENABLED |
			QDC_V965A::BITSET_2_LOW_THRESHOLD_ENABLED); 
		
	// enable measurement via output 1 of io1
	cout << "starting measurement" << endl; 
	io1.setO1(1);
	TM_START();
	for (unsigned loops = 0; loops < 200000; ) {
		/*
		if (qdc1.BufferFull()) {
			buf_full_2++;
			//cout << "QDC buffer full (QDC info)" << endl;
		}
								 */		
		unsigned new_ev_counter = counter1.getA();
		unsigned delta = new_ev_counter - ev_counter;
		//unsigned delta = 32;
		//usleep(10);
		
		if (delta >= 32) {
			//cout << "QDC buffer full (delta==32)" << endl;
			buf_full_1++;
		}
		
		// counter may have more events than QDC buffer
		if (delta > 0)
			delta--;
				
		// QDC buffer size is 32
		if (delta > 32) {
			delta = 32;
			cout << "QDC buffer overflow" << endl;
			// actually, this should NEVER happen.
		}
		
		/*		
		// is there a 8 bit limit for DMA/BLT transfers?  (7*34=238)
		if (delta > 7)
			delta = 7;
		
		// is there a 9 bit limit for DMA/BLT transfers?  (15*34=510)
		if (delta > 15)
			delta = 15;
		*/
					
		// is there a 10 bit limit for DMA/BLT transfers?  (30*34=1020)
		if (delta > 30)
			delta = 30;
					
		
		if (delta >= 2) {
				
		//if (qdc1.BufferFull()) {
			
			// stop measurement
			//io1.setO1(0);
			
//			cout << "." << flush;
/*
			cout << endl 
				<< "timer:" 
				<< " hitcounter = " << tc1.getWords()
				<< " status = " << setw(4) << setfill('0') << hex << tc1.getStatus() 
				<< endl
				<< "counter:" << dec << counter1.getA()
				<< endl;
*/		
#if 1
			try {
				qdc1.ReadEvents(ev, delta);
			}
			catch(IOError ioe) {
				cout << "Catched IOError:" << endl;
				cout << ioe.getMsg() << endl;
				cout << "... while trying to read " << delta << " events from QDC." << endl;
				cout << "event counter: " << new_ev_counter << endl;
				cout << "buffer was full " << buf_full_1 << "/" << buf_full_2 << " times" << endl;
				cout << "QDC status: 0x" << hex << qdc1.GetStatus1() << ", 0x" << hex << qdc1.GetStatus2() << dec << endl;
				cout << "Abort after " << loops << " events." << endl;
				rf.Write();
				rf.Close();
				exit(-41);
			}
#endif				
			// save results
			for (unsigned i = 0; i < delta; i++) {
				//qdc1.ReadEventFast(&(ev[i]));
				
				// we could also use tc1.ReadEvents
				time = tc1.readFIFO();
				dt = time - prev_time;
				
				// FIXME: that code is plain ugly!
				//u_int16_t *qdc = (u_int16_t*)(&ev);
				u_int16_t *qdc = (u_int16_t*)&(ev[i]);
				for (int k=0; k<8; k++)
					adc[k] = qdc[k];
								
				f << " " << adc[0] << " " << adc[1] << " " << adc[2] << " " << adc[3]
					   << " " << adc[4] << " " << adc[5] << " " << adc[6] << " " << adc[7]
					   << " " << time << " " << dt << endl;
				tree->Fill();
				prev_time = time;
			
				loops++;
				ev_counter++;
			}
			
			
			// FIXME: why do we need to reset / clear the FIFOs??
			// answer: because they're nor decremented during read-out of the FIFOs
			//tc1.reset();
			
			// restart
			//io1.setO1(1);
		}
	}
	TM_STOP();
		
	// disable gate of adc1 via output 1 of io1
	cout << "stop measurement" << endl; 
	io1.setO1(0);
	//cout << "hm "<< tc1.readFIFO() << endl;
	
	cout << "buffer was full " << buf_full_1 << "/" << buf_full_2 << " times" << endl;

	cerr << "# cpu_freq: " << tm_cpu_freq << endl
			<< "  cpu_ticks: " << tm_delta << endl
			<< " delta time: " << tm_secs << "sec  = "
			<< tm_secs*1000 << " ms" << endl << endl;


	f.close();
	rf.Write();
	rf.Close();
	
			
	// output counter values
    cout << "counter1.a == " << counter1.getA() << endl;
    cout << "counter1.b == " << counter1.getB() << endl;
    cout << "counter1.c == " << counter1.getC() << endl << endl;
                
    usleep(100);
  }
  catch( IOError ioe ) {
    cout << "catched IOError expection: " << ioe.getMsg() << endl 
	 << "param=" << ioe.getParam() << endl;
  }
    

  close(crate);

  return 0;
}
